package ast

import (
	"strings"

	"gitee.com/u-language/u-language/ucom/astdata"
	"gitee.com/u-language/u-language/ucom/enum"
	"gitee.com/u-language/u-language/ucom/errcode"
	"gitee.com/u-language/u-language/ucom/lex"
)

// 对象类型枚举
type ObjKind int

const (
	NoKind ObjKind = 1 << iota
	INTOBJ
	FLOATOBJ
	BoolObj
	StringObj
	SymbolObj
	LeaObj
	DerefObj
	StructPtr
	NilObj
	EnumObj
	TypeObj
)

var (
	ObjKindSrtMap = [...]string{
		NoKind:    "nokind (未定义的枚举)",
		INTOBJ:    "intobj (整数)",
		FLOATOBJ:  "floatobj (浮点数)",
		BoolObj:   "boolobj (布尔值)",
		StringObj: "stringobj（字符串）",
		SymbolObj: "SymbolObj (符号)",
		LeaObj:    "leaObj (取地址)",
		DerefObj:  "derefObj (解引用)",
		StructPtr: "StructPtr (结构体指针)",
		NilObj:    "NilObj (指针的零值)",
		EnumObj:   "EnumObj (枚举值)",
		TypeObj:   "TypeObj(类型)",
	}
)

func (o ObjKind) String() string {
	var buf strings.Builder
	try_equal(o, INTOBJ, &buf)
	try_equal(o, FLOATOBJ, &buf)
	try_equal(o, BoolObj, &buf)
	try_equal(o, StringObj, &buf)
	try_equal(o, SymbolObj, &buf)
	try_equal(o, LeaObj, &buf)
	try_equal(o, DerefObj, &buf)
	try_equal(o, StructPtr, &buf)
	try_equal(o, NilObj, &buf)
	try_equal(o, EnumObj, &buf)
	try_equal(o, TypeObj, &buf)
	return buf.String()
}

func try_equal(k ObjKind, eq ObjKind, buf *strings.Builder) {
	if k&eq != 0 {
		buf.WriteString(ObjKindSrtMap[eq])
	}
}

var (
	TypeEnumStrMap = map[string]struct{}{
		"int":              struct{}{},
		"string":           struct{}{},
		"bool":             struct{}{},
		"float":            struct{}{},
		enum.UnsafePointer: struct{}{},
		"any":              struct{}{},
	}
)

// 预定义的函数或视为函数调用的类型转换
var Builtin_func_info = map[string]SymbolInfo{
	enum.Printf:        NewSymbolInfo_Func(&FuncInfo{Name: enum.Printf, Parame: []astdata.Parame{astdata.NewNameAndType("format", NewObject(TypeObj, enum.String))}}),
	enum.Float:         NewSymbolInfo_Func(&FuncInfo{Name: enum.Float, RetValue: []astdata.RetValue{astdata.NewNameAndType("", NewObject(TypeObj, enum.Float))}}),
	enum.Int:           NewSymbolInfo_Func(&FuncInfo{Name: enum.Int, RetValue: []astdata.RetValue{astdata.NewNameAndType("", NewObject(TypeObj, enum.Int))}}),
	enum.Malloc:        NewSymbolInfo_Func(&FuncInfo{Name: enum.Malloc, Parame: []astdata.Parame{astdata.NewNameAndType("type", NewObject(TypeObj, "T"))}, RetValue: []astdata.RetValue{astdata.NewNameAndType("ptr", NewObject(TypeObj, "&T"))}}),
	enum.Free:          NewSymbolInfo_Func(&FuncInfo{Name: enum.Free, Parame: []astdata.Parame{astdata.NewNameAndType("ptr", NewObject(TypeObj, "&T"))}}),
	enum.UnsafeAdd:     NewSymbolInfo_Func(&FuncInfo{Name: enum.UnsafeAdd, Parame: []astdata.Parame{astdata.NewNameAndType("ptr", NewObject(TypeObj, "&T")), astdata.NewNameAndType("offset", NewObject(TypeObj, enum.Int))}, RetValue: []astdata.RetValue{astdata.NewNameAndType("ptr", NewObject(TypeObj, "&T"))}}),
	enum.UnsafeConvert: NewSymbolInfo_Func(&FuncInfo{Name: enum.UnsafeConvert, Parame: []astdata.Parame{astdata.NewNameAndType("ptr", NewObject(TypeObj, enum.UnsafePointer)), astdata.NewNameAndType("type", NewObject(TypeObj, "T"))}, RetValue: []astdata.RetValue{astdata.NewNameAndType("ptr", NewObject(TypeObj, "&T"))}}),
	enum.UnsafeSizeof:  NewSymbolInfo_Func(&FuncInfo{Name: enum.UnsafeSizeof, Parame: []astdata.Parame{astdata.NewNameAndType("type", NewObject(TypeObj, "T"))}, RetValue: []astdata.RetValue{astdata.NewNameAndType("size", NewObject(TypeObj, enum.Int))}}),
	enum.MallocSize:    NewSymbolInfo_Func(&FuncInfo{Name: enum.MallocSize, Parame: []astdata.Parame{astdata.NewNameAndType("size", NewObject(TypeObj, enum.Int))}, RetValue: []astdata.RetValue{astdata.NewNameAndType("ptr", NewObject(TypeObj, enum.UnsafePointer))}}),
	enum.MemPoolFree:   SymbolInfo{},
	enum.MemPoolNew:    SymbolInfo{},
	enum.UnsafePointer: SymbolInfo{},
}

var codeBlock_errmap = map[string]errcode.ErrCode{
	enum.Func:     errcode.FUNCErr,
	"if":          errcode.IfErr,
	"else":        errcode.ElseErr,
	"for":         errcode.ForErr,
	enum.Switch:   errcode.SwitchErr,
	enum.Method:   errcode.MethodErr,
	enum.AutoFree: errcode.AutoFreeErr,
}
var token_typ_err = map[lex.TokenType]errcode.ErrCode{
	lex.RPAREN: errcode.UnexpectedRPAREN,
	lex.Nil:    errcode.UnexpectedNil,
	lex.Comma:  errcode.UnexpectedComma,
	lex.LPAREN: errcode.UnexpectedLPAREN,
}
